﻿using System;
using Sharp7;

namespace Zhy.Sharp7Demo
{
    /// <summary>
    /// Sharp7 PLC通信帮助类
    /// 提供与西门子S7系列PLC的通信功能
    /// </summary>
    public class Sharp7Helper : IDisposable
    {
        /// <summary>
        /// S7Client对象
        /// </summary>
        private S7Client _client;
        /// <summary>
        /// 是否已连接
        /// </summary>
        private bool _isConnected = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        public Sharp7Helper()
        {
            _client = new S7Client(); // 创建S7Client对象
        }

        /// <summary>
        /// 连接到PLC
        /// </summary>
        /// <param name="ip">PLC IP地址</param>
        /// <param name="rack">机架号</param>
        /// <param name="slot">插槽号</param>
        /// <returns>连接是否成功</returns>
        public bool Connect(string ip, int rack = 0, int slot = 1)
        {
            try
            {
                int result = _client.ConnectTo(ip, rack, slot); // 连接到PLC
                if (result == 0)
                {
                    _isConnected = true; // 设置已连接状态
                    return true;
                }
                else
                {
                    Console.WriteLine($"连接失败，错误代码: {result}"); // 输出错误信息
                    return false;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"连接异常: {ex.Message}"); // 输出异常信息
                return false;
            }
        }

        /// <summary>
        /// 断开与PLC的连接
        /// </summary>
        public void Disconnect()
        {
            if (_isConnected)
            {
                _client.Disconnect(); // 断开连接
                _isConnected = false;
            }
        }

        /// <summary>
        /// 读取DB块中的布尔值（线圈状态）
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="bitOffset">位偏移量(0-7)</param>
        /// <returns>读取的布尔值</returns>
        public bool ReadBool(int dbNumber, int byteOffset, int bitOffset)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC"); // 未连接

            byte[] buffer = new byte[1]; // 创建字节缓冲区
            int result = _client.DBRead(dbNumber, byteOffset, 1, buffer); // 读取数据

            if (result == 0)
            {
                return S7.GetBitAt(buffer, 0, bitOffset); // 返回布尔值
            }
            else
            {
                throw new Exception($"读取布尔值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 写入布尔值到DB块（线圈控制）
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="bitOffset">位偏移量(0-7)</param>
        /// <param name="value">要写入的值</param>
        public void WriteBool(int dbNumber, int byteOffset, int bitOffset, bool value)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC"); // 未连接

            byte[] buffer = new byte[1];
            // 先读取当前字节值
            int readResult = _client.DBRead(dbNumber, byteOffset, 1, buffer);

            if (readResult == 0)
            {
                S7.SetBitAt(buffer, 0, bitOffset, value);
                int writeResult = _client.DBWrite(dbNumber, byteOffset, 1, buffer);

                if (writeResult != 0)
                {
                    throw new Exception($"写入布尔值失败，错误代码: {writeResult}");
                }
            }
            else
            {
                throw new Exception($"读取当前值失败，错误代码: {readResult}");
            }
        }

        /// <summary>
        /// 读取DB块中的16位整数
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <returns>读取的整数值</returns>
        public short ReadInt(int dbNumber, int byteOffset)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[2];
            int result = _client.DBRead(dbNumber, byteOffset, 2, buffer);

            if (result == 0)
            {
                return S7.GetIntAt(buffer, 0);
            }
            else
            {
                throw new Exception($"读取整数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 写入16位整数到DB块
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="value">要写入的值</param>
        public void WriteInt(int dbNumber, int byteOffset, short value)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[2];
            S7.SetIntAt(buffer, 0, value);

            int result = _client.DBWrite(dbNumber, byteOffset, 2, buffer);
            if (result != 0)
            {
                throw new Exception($"写入整数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 读取DB块中的32位整数
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <returns>读取的整数值</returns>
        public int ReadDInt(int dbNumber, int byteOffset)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[4];
            int result = _client.DBRead(dbNumber, byteOffset, 4, buffer);

            if (result == 0)
            {
                return S7.GetDIntAt(buffer, 0);
            }
            else
            {
                throw new Exception($"读取双整数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 写入32位整数到DB块
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="value">要写入的值</param>
        public void WriteDInt(int dbNumber, int byteOffset, int value)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[4];
            S7.SetDIntAt(buffer, 0, value);

            int result = _client.DBWrite(dbNumber, byteOffset, 4, buffer);
            if (result != 0)
            {
                throw new Exception($"写入双整数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 读取DB块中的浮点数
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <returns>读取的浮点数值</returns>
        public float ReadReal(int dbNumber, int byteOffset)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[4];
            int result = _client.DBRead(dbNumber, byteOffset, 4, buffer);

            if (result == 0)
            {
                return S7.GetRealAt(buffer, 0);
            }
            else
            {
                throw new Exception($"读取浮点数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 写入浮点数到DB块
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="value">要写入的值</param>
        public void WriteReal(int dbNumber, int byteOffset, float value)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[4];
            S7.SetRealAt(buffer, 0, value);

            int result = _client.DBWrite(dbNumber, byteOffset, 4, buffer);
            if (result != 0)
            {
                throw new Exception($"写入浮点数值失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 读取DB块中的字符串
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="maxLength">最大长度</param>
        /// <returns>读取的字符串</returns>
        public string ReadString(int dbNumber, int byteOffset, int maxLength)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[maxLength + 2]; // 包含长度字节
            int result = _client.DBRead(dbNumber, byteOffset, buffer.Length, buffer);

            if (result == 0)
            {
                return S7.GetStringAt(buffer, 0);
            }
            else
            {
                throw new Exception($"读取字符串失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 写入字符串到DB块
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="value">要写入的字符串</param>
        public void WriteString(int dbNumber, int byteOffset, string value)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[value.Length + 2];
            S7.SetStringAt(buffer, 0, value.Length, value);

            int result = _client.DBWrite(dbNumber, byteOffset, buffer.Length, buffer);
            if (result != 0)
            {
                throw new Exception($"写入字符串失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 批量读取多个字节
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="length">读取长度</param>
        /// <returns>读取的字节数组</returns>
        public byte[] ReadBytes(int dbNumber, int byteOffset, int length)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            byte[] buffer = new byte[length];
            int result = _client.DBRead(dbNumber, byteOffset, length, buffer);

            if (result == 0)
            {
                return buffer;
            }
            else
            {
                throw new Exception($"批量读取失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 批量写入多个字节
        /// </summary>
        /// <param name="dbNumber">DB块编号</param>
        /// <param name="byteOffset">起始地址</param>
        /// <param name="data">要写入的字节数组</param>
        public void WriteBytes(int dbNumber, int byteOffset, byte[] data)
        {
            if (!_isConnected) throw new InvalidOperationException("未连接到PLC");

            int result = _client.DBWrite(dbNumber, byteOffset, data.Length, data);
            if (result != 0)
            {
                throw new Exception($"批量写入失败，错误代码: {result}");
            }
        }

        /// <summary>
        /// 获取连接状态
        /// </summary>
        public bool IsConnected => _isConnected;

        /// <summary>
        /// 获取最后一次操作的错误代码
        /// </summary>
        public int LastError => _client.LastError();

        /// <summary>
        /// 获取最后一次操作的错误描述
        /// </summary>
        public string LastErrorText => _client.ErrorText(LastError);

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Disconnect();
            _client = null;
        }
    }
}
